import { getBusinessObject, is } from 'bpmn-js/lib/util/ModelUtil'
import { useSettingStore } from '@/store/bpmnProcess/settingStore'
import { useModelerStore } from '@/store/bpmnProcess/modelerStore'
import { getExtensionElementsList, createModdleElement } from './bpmnPropertyUtils/extensionElements'

export function isMultiInstanceSupported(element) {
  const prefix = useSettingStore().editor.processEngine
  const loopCharacteristics = getLoopCharacteristics(element)
  return !!loopCharacteristics && is(loopCharacteristics, `${prefix}:Collectable`)
}

export function getMultiInstanceData(element) {
  const prefix = useSettingStore().editor.processEngine
  const loopCharacteristics = getLoopCharacteristics(element)
  return {
    loopCardinality: getLoopCardinalityValue(element) || '',
    completionCondition: getCompletionConditionValue(element) || '',
    collection: getProperty(element, `${prefix}:collection`) || '',
    elementVariable: getProperty(element, `${prefix}:elementVariable`) || '',
    asyncBefore: isAsyncBefore(loopCharacteristics),
    asyncAfter: isAsyncAfter(loopCharacteristics),
    exclusive: isExclusive(loopCharacteristics),
    failedJobRetryTimeCycle: getFailedJobRetryTimeCycle(element) || ''
  }
}

export function setLoopCardinalityValue(element, value) {
  const modeling = useModelerStore().getModeling
  const res = updateFormalExpression(element, 'loopCardinality', value)
  modeling.updateModdleProperties(res.element, res.moddleElement, res.properties)
}

export function setCompletionConditionValue(element, value) {
  const modeling = useModelerStore().getModeling
  const res = updateFormalExpression(element, 'completionCondition', value)
  modeling.updateModdleProperties(res.element, res.moddleElement, res.properties)
}

export function setCollectionValue(element, value) {
  const prefix = useSettingStore().editor.processEngine
  const modeling = useModelerStore().getModeling
  const loopCharacteristics = getLoopCharacteristics(element)
  modeling.updateModdleProperties(element, loopCharacteristics, { [`${prefix}:collection`]: value })
}

export function setElementVariableValue(element, value) {
  const prefix = useSettingStore().editor.processEngine
  const modeling = useModelerStore().getModeling
  const loopCharacteristics = getLoopCharacteristics(element)
  modeling.updateModdleProperties(element, loopCharacteristics, { [`${prefix}:elementVariable`]: value })
}

export function setAsynchronousBeforeValue(element, value) {
  const prefix = useSettingStore().editor.processEngine
  const modeling = useModelerStore().getModeling
  const loopCharacteristics = getLoopCharacteristics(element)
  modeling.updateModdleProperties(element, loopCharacteristics, { [`${prefix}:asyncBefore`]: value, [`${prefix}:async`]: undefined })
}

export function setAsynchronousAfterValue(element, value) {
  const prefix = useSettingStore().editor.processEngine
  const modeling = useModelerStore().getModeling
  const loopCharacteristics = getLoopCharacteristics(element)
  modeling.updateModdleProperties(element, loopCharacteristics, { [`${prefix}:asyncAfter`]: value })
}

export function setExclusiveValue(element, value) {
  const prefix = useSettingStore().editor.processEngine
  const modeling = useModelerStore().getModeling
  const loopCharacteristics = getLoopCharacteristics(element)
  modeling.updateModdleProperties(element, loopCharacteristics, { [`${prefix}:exclusive`]: value })
}

export function setRetryTimeCycleValue(element, value) {
  const prefix = useSettingStore().editor.processEngine
  const modeling = useModelerStore().getModeling
  const loopCharacteristics = getLoopCharacteristics(element)
  let extensionElements = loopCharacteristics.get('extensionElements')
  // (1) ensure extension elements
  if (!extensionElements) {
    extensionElements = createModdleElement('bpmn:ExtensionElements', { values: [] }, loopCharacteristics)
    modeling.updateModdleProperties(element, loopCharacteristics, { extensionElements })
  }
  // (2) ensure failedJobRetryTimeCycle
  let failedJobRetryTimeCycle = getExtensionElementsList(loopCharacteristics, `${prefix}:FailedJobRetryTimeCycle`)[0]
  if (!failedJobRetryTimeCycle) {
    failedJobRetryTimeCycle = createModdleElement(`${prefix}:FailedJobRetryTimeCycle`, {}, extensionElements)
    modeling.updateModdleProperties(loopCharacteristics, extensionElements, { values: [...extensionElements.get('values'), failedJobRetryTimeCycle] })
  }
  // (3) update failedJobRetryTimeCycle value
  modeling.updateModdleProperties(element, failedJobRetryTimeCycle, { body: value })
}

function getLoopCardinalityValue(element) {
  const loopCardinality = getProperty(element, 'loopCardinality')
  return getBody(loopCardinality)
}

function getCompletionConditionValue(element) {
  const completionCondition = getProperty(element, 'completionCondition')
  return getBody(completionCondition)
}

function getProperty(element, propertyName) {
  const loopCharacteristics = getLoopCharacteristics(element)
  return loopCharacteristics && loopCharacteristics.get(propertyName)
}

function getLoopCharacteristics(element) {
  const bo = getBusinessObject(element)
  return bo.loopCharacteristics
}

function isAsyncBefore(bo) {
  const prefix = useSettingStore().editor.processEngine
  return !!(bo.get(`${prefix}:asyncBefore`) || bo.get(`${prefix}:async`))
}

function isAsyncAfter(bo) {
  const prefix = useSettingStore().editor.processEngine
  return !!bo.get(`${prefix}:asyncAfter`)
}

function isExclusive(bo) {
  const prefix = useSettingStore().editor.processEngine
  return !!bo.get(`${prefix}:exclusive`)
}

function getFailedJobRetryTimeCycle(element) {
  const prefix = useSettingStore().editor.processEngine
  const loopCharacteristics = getLoopCharacteristics(element)
  const failedJobRetryTimeCycle = getExtensionElementsList(loopCharacteristics, `${prefix}:FailedJobRetryTimeCycle`)[0]
  return failedJobRetryTimeCycle && failedJobRetryTimeCycle.body
}

function getBody(expression) {
  return expression && expression.get('body')
}

function isAsync(bo) {
  return isAsyncAfter(bo) || isAsyncBefore(bo)
}

function updateFormalExpression(element, propertyName, newValue) {
  const loopCharacteristics = getLoopCharacteristics(element)
  const expressionProps = {}
  if (!newValue) {
    // remove formal expression
    expressionProps[propertyName] = undefined
    return {
      element,
      moddleElement: loopCharacteristics,
      properties: expressionProps
    }
  }
  const existingExpression = loopCharacteristics.get(propertyName)
  if (!existingExpression) {
    // add formal expression
    expressionProps[propertyName] = createFormalExpression(loopCharacteristics, newValue)
    return {
      element,
      moddleElement: loopCharacteristics,
      properties: expressionProps
    }
  }

  // edit existing formal expression
  return {
    element,
    moddleElement: existingExpression,
    properties: {
      body: newValue
    }
  }
}

function createFormalExpression(parent, body) {
  return createModdleElement('bpmn:FormalExpression', { body: body }, parent)
}
